home *** CD-ROM | disk | FTP | other *** search
- ----eaea.c----
- /*
- * ae.c Anthony's Editor May '92
- *
- * Public Domain 1991, 1992 by Anthony Howe. All rights released.
- */
-
- #include <ctype.h>
- #include <curses.h>
- #include <stdio.h>
- #include <string.h>
-
- #ifndef BUF
- #define BUF 32767
- #endif /* BUF */
-
- #ifndef HUP
- #define HUP "ae.hup"
- #endif /* HUP */
-
- #define HELP_LINE 7
-
- typedef struct keytable_t {
- int key;
- void (*func)();
- } keytable_t;
-
- int done;
- int row, col;
- int point, page, epage;
- int input;
- int helpline;
- char buf[BUF];
- char *ebuf;
- char *gap = buf;
- char *egap;
- char *filename;
- keytable_t *table;
-
- /*
- * The following assertions must be maintained.
- *
- * o buf <= gap <= egap <= ebuf
- * If gap == egap then the buffer is full.
- *
- * o cursor = ptr(point) and cursor < gap or egap <= cursor
- *
- * o page <= point < epage
- *
- * o 0 <= point <= pos(ebuf) <= BUF
- *
- *
- * Memory representation of the file:
- *
- * low buf -->+----------+
- * | front |
- * | of file |
- * gap -->+----------+<-- character not in file
- * | hole |
- * egap -->+----------+<-- character in file
- * | back |
- * | of file |
- * high ebuf -->+----------+<-- character not in file
- *
- *
- * point & gap
- *
- * The Point is the current cursor position while the Gap is the
- * position where the last edit operation took place. The Gap is
- * ment to be the cursor but to avoid shuffling characters while
- * the cursor moves it is easier to just move a pointer and when
- * something serious has to be done then you move the Gap to the
- * Point.
- *
- *
- * Use of stdio for portability.
- *
- * Stdio will handle the necessary conversions of text files to
- * and from a machine specific format. Things like fixed length
- * records; CRLF mapping into <newline> (\n) and back again;
- * null padding; control-Z end-of-file marks; and other assorted
- * bizare issues that appear on many unusual machines.
- *
- * AE is meant to be simple in both code and usage. With that
- * in mind certain assumptions are made.
- *
- * Reading: If a file can not be opened, assume that it is a
- * new file. If an error occurs, fall back to a safe state and
- * assume an empty file. fread() is typed size_t which is an
- * unsigned number. Zero (0) would indicate a read error or an
- * empty file. A return value less than BUF is alright, since
- * we asked for the maximum allowed.
- *
- * Writing: If the file can not be opened or a write error occurs,
- * then we scramble and save the user's changes in a file called
- * ae.hup. If ae.hup fails to open or a write error occurs, then
- * we assume that shit happens.
- *
- */
-
- int adjust();
- int nextline();
- int pos();
- int prevline();
- int save();
- char *ptr();
-
- void backsp();
- void bottom();
- void delete();
- void display();
- void down();
- void file();
- void help();
- void insert();
- void insert_mode();
- void left();
- void lnbegin();
- void lnend();
- void movegap();
- void pgdown();
- void pgup();
- void redraw();
- void right();
- void quit();
- void flip();
- void top();
- void up();
- void wleft();
- void wright();
-
- #if TERMCAP
- /*
- * Function Key Support for BSD CURSES.
- *
- * BSD CURSES does not support function keys as nicely as System V
- * or XPG CURSES. So I've provided some functions that can decode
- * multi-byte function keys that are commonly supported by BSD's
- * termcap file.
- *
- * The KEY_xxxx macro constants are not defined in the same manner
- * as System V or XPG. Also the key_table[] can be customised to
- * support additional key sequences that might not be supported by
- * termcap. Create a new key code and provide the key sequence
- * string; initkey() will not alter the provided sequence provided
- * it doesn't match a termcap capability name (see KEY_BACKSPACE).
- */
-
- #include <sys/types.h>
-
- int initkey();
- int getkey();
-
- #define KEY_DOWN (-11)
- #define KEY_UP (-12)
- #define KEY_LEFT (-13)
- #define KEY_RIGHT (-14)
- #define KEY_BACKSPACE (-15)
- #define KEY_DC (-16)
- #define KEY_F0 (-20)
- #define KEY_F(n) (KEY_F0-(n))
-
- typedef struct key_entry_t {
- short code;
- char *entry;
- } key_entry_t;
-
- char key_buffer[1024];
-
- key_entry_t key_table[] = {
- { KEY_DOWN, "kd" },
- { KEY_UP, "ku" },
- { KEY_LEFT, "kl" },
- { KEY_RIGHT, "kr" },
- { KEY_BACKSPACE, "\b" },
- { KEY_DC, "\177" },
- { KEY_F(0), "k0" },
- { KEY_F(1), "k1" },
- { KEY_F(2), "k2" },
- { KEY_F(3), "k3" },
- { KEY_F(4), "k4" },
- { KEY_F(5), "k5" },
- { KEY_F(6), "k6" },
- { KEY_F(7), "k7" },
- { KEY_F(8), "k8" },
- { KEY_F(9), "k9" },
- { 0, NULL }
- };
-
- int
- initkey()
- {
- key_entry_t *k;
- char *ptr, *kbuf, *tname;
- static char buffer[1024];
-
- if ((tname = (char*) getenv("TERM")) == NULL
- || tgetent(buffer, tname) != 1)
- return (0);
- for (kbuf = key_buffer, k = key_table; k->entry != NULL; ++k) {
- ptr = (char*) tgetstr(k->entry, &kbuf);
- if (ptr != NULL)
- k->entry = ptr;
- }
- return (1);
- }
-
- int
- getkey()
- {
- key_entry_t *k;
- int submatch;
- static char buffer[128];
- static char *record = buffer;
-
- /* If recorded bytes remain, return next recorded byte. */
- if (*record != '\0')
- return (*record++);
- /* Reset record buffer. */
- record = buffer;
- do {
- /* Read and record one byte. */
- *record++ = getch();
- *record = '\0';
-
- /* If recorded bytes match any multi-byte sequence... */
- for (k = key_table, submatch = 0; k->entry != NULL; ++k) {
- char *p, *q;
- for (p = buffer, q = k->entry; *p == *q; ++p, ++q) {
- if (*p == '\0') {
- /* Return extended key code. */
- return (k->code);
- }
- }
- if (*p == '\0') {
- /* Recorded bytes match anchored substring. */
- submatch = 1;
- }
- }
- /* If recorded bytes matched an anchored substring, loop. */
- } while (submatch);
- /* Return first recorded byte. */
- record = buffer;
- return (*record++);
- }
-
- #else /* not TERMCAP */
-
- #define initkey() keypad(stdscr,1)
- #define getkey() getch()
-
- #endif /* TERMCAP */
-
- /* ASCII Control Codes */
- #undef CTRL
- #define CTRL(x) ((x) & 0x1f)
- #define DEL 0x7f
-
- char help_ea[] = "\
- Left, right, up, down\tarrow keys\tBeginning and end of line\t^A ^D\n\
- Word left and right\t^W ^E\t\tTop and bottom of file\t\t^T ^B\n\
- Page up and down\t^P ^N\t\tDelete left and right\tbackspace DEL\n\
- Insert\t\t\ttyped keys\tHelp on and off\t\t\tF1\n\
- Save file\t\t^F\t\tRedraw\t\t\t\t^R\n\
- Quit\t\t\t^C\t\tFlip to VI-style\t\t^Z\n\
- ....5...10....5...20....5...30....5...40....5...50....5...60....5...70....5...80";
-
- keytable_t modeless[] = {
- { KEY_LEFT, left },
- { KEY_RIGHT, right },
- { KEY_DOWN, down },
- { KEY_UP, up },
- { CTRL('w'), wleft },
- { CTRL('e'), wright },
- { CTRL('n'), pgdown },
- { CTRL('p'), pgup },
- { CTRL('a'), lnbegin },
- { CTRL('d'), lnend },
- { CTRL('t'), top },
- { CTRL('b'), bottom },
- { KEY_BACKSPACE, backsp },
- { '\b', backsp },
- { KEY_DC, delete },
- { DEL, delete },
- { CTRL('f'), file },
- { CTRL('r'), redraw },
- { CTRL('c'), quit },
- { CTRL('z'), flip },
- { KEY_F(1), help },
- { 0, insert }
- };
-
- char help_ae[] = "\
- Left, right, up, down\th j k l \tBeginning and end of line\t[ ]\n\
- Word left and right\tH L\t\tTop and bottom of file\t\tt b\n\
- Page up and down\tJ K\t\tDelete left and right\t\tX x\n\
- Insert on and off\ti ^L\t\tHelp on and off\t\t\t? \n\
- Save file\t\tF\t\tRedraw\t\t\t\tR\n\
- Quit\t\t\tQ\t\tFlip to EMACS-style\t\tZ\n\
- ....5...10....5...20....5...30....5...40....5...50....5...60....5...70....5...80";
-
- keytable_t modual[] = {
- { 'h', left },
- { 'j', down },
- { 'k', up },
- { 'l', right },
- { 'H', wleft },
- { 'J', pgdown },
- { 'K', pgup },
- { 'L', wright },
- { '[', lnbegin },
- { ']', lnend },
- { 't', top },
- { 'b', bottom },
- { 'i', insert_mode },
- { 'x', delete },
- { 'X', backsp },
- { 'F', file },
- { 'R', redraw },
- { 'Q', quit },
- { 'Z', flip },
- { '?', help },
- { 0, movegap }
- };
-
- #ifdef POSIX
-
- #include <termios.h>
-
- /*
- * Set the desired input mode.
- *
- * FALSE enables immediate character processing (disable signals
- * and line processing.) TRUE enables line processing and signals
- * (disables immediate character processing). In either case flow
- * control (XON/XOFF) is still active.
- *
- * If the termios function calls fail, then fall back on using
- * CURSES' raw()/noraw() functions; however flow control will be
- * affected.
- */
- void
- lineinput(bf)
- int bf;
- {
- int error;
- struct termios term;
- error = tcgetattr(fileno(stdin), &term) < 0;
- if (!error) {
- if (bf)
- term.c_lflag |= ISIG | ICANON;
- else
- term.c_lflag &= ~(ISIG | ICANON);
- error = tcsetattr(fileno(stdin), TCSANOW, &term) < 0;
- }
- /* Fall back on CURSES functions that do almost what we need if
- * either tcgetattr() or tcsetattr() fail.
- */
- if (error) {
- if (bf)
- noraw();
- else
- raw();
- }
- }
-
- #else /* not POSIX */
-
- #define lineinput(bf) (bf ? noraw() : raw());
-
- #endif /* POSIX */
-
-
- char *
- ptr(offset)
- int offset;
- {
- if (offset < 0)
- return (buf);
- return (buf+offset + (buf+offset < gap ? 0 : egap-gap));
- }
-
- int
- pos(pointer)
- char *pointer;
- {
- return (pointer-buf - (pointer < egap ? 0 : egap-gap));
- }
-
- void
- top()
- {
- point = 0;
- }
-
- void
- bottom()
- {
- epage = point = pos(ebuf);
- }
-
- void
- quit()
- {
- done = 1;
- }
-
- void
- redraw()
- {
- clear();
- if (helpline == HELP_LINE)
- mvaddstr(0, 0, table == modual ? help_ae : help_ea);
- display();
- }
-
- void
- movegap()
- {
- char *p = ptr(point);
- while (p < gap)
- *--egap = *--gap;
- while (egap < p)
- *gap++ = *egap++;
- point = pos(egap);
- }
-
- int
- prevline(offset)
- int offset;
- {
- char *p;
- while (buf < (p = ptr(--offset)) && *p != '\n')
- ;
- return (buf < p ? ++offset : 0);
- }
-
- int
- nextline(offset)
- int offset;
- {
- char *p;
- while ((p = ptr(offset++)) < ebuf && *p != '\n')
- ;
- return (p < ebuf ? offset : pos(ebuf));
- }
-
- int
- adjust(offset, column)
- int offset, column;
- {
- char *p;
- int i = 0;
- while ((p = ptr(offset)) < ebuf && *p != '\n' && i < column) {
- i += *p == '\t' ? 8-(i&7) : 1;
- ++offset;
- }
- return (offset);
- }
-
- void
- left()
- {
- if (0 < point)
- --point;
- }
-
- void
- right()
- {
- if (point < pos(ebuf))
- ++point;
- }
-
- void
- up()
- {
- point = adjust(prevline(prevline(point)-1), col);
- }
-
- void
- down()
- {
- point = adjust(nextline(point), col);
- }
-
- void
- lnbegin()
- {
- point = prevline(point);
- }
-
- void
- lnend()
- {
- point = nextline(point);
- left();
- }
-
- void
- wleft()
- {
- char *p;
- while (!isspace(*(p = ptr(point))) && buf < p)
- --point;
- while (isspace(*(p = ptr(point))) && buf < p)
- --point;
- }
-
- void
- pgdown()
- {
- page = point = prevline(epage-1);
- while (0 < row--)
- down();
- epage = pos(ebuf);
- }
-
- void
- pgup()
- {
- int i = LINES;
- while (0 < --i) {
- page = prevline(page-1);
- up();
- }
- }
-
- void
- wright()
- {
- char *p;
- while (!isspace(*(p = ptr(point))) && p < ebuf)
- ++point;
- while (isspace(*(p = ptr(point))) && p < ebuf)
- ++point;
- }
-
- void
- insert()
- {
- movegap();
- if (gap < egap)
- *gap++ = input == '\r' ? '\n' : input;
- point = pos(egap);
- }
-
- void
- insert_mode()
- {
- int ch;
- movegap();
- while ((ch = getkey()) != '\f') {
- if (ch == '\b') {
- if (buf < gap)
- --gap;
- } else if (gap < egap) {
- *gap++ = ch == '\r' ? '\n' : ch;
- }
- point = pos(egap);
- display();
- }
- }
-
- void
- backsp()
- {
- movegap();
- if (buf < gap)
- --gap;
- point = pos(egap);
- }
-
- void
- delete()
- {
- movegap();
- if (egap < ebuf)
- point = pos(++egap);
- }
-
- void
- file()
- {
- if (!save(filename))
- save(HUP);
- }
-
- int
- save(fn)
- char *fn;
- {
- FILE *fp;
- int i, ok;
- size_t length;
- fp = fopen(fn, "w");
- if ((ok = fp != NULL)) {
- i = point;
- point = 0;
- movegap();
- length = (size_t) (ebuf-egap);
- ok = fwrite(egap, sizeof (char), length, fp) == length;
- (void) fclose(fp);
- point = i;
- }
- return (ok);
- }
-
- void
- flip()
- {
- table = table == modual ? modeless : modual;
- if (helpline == HELP_LINE)
- mvaddstr(0, 0, table == modual ? help_ae: help_ea);
- }
-
- void
- help()
- {
- helpline = helpline == 0 ? HELP_LINE : 0;
- redraw();
- }
-
- void
- display()
- {
- char *p;
- int i, j;
- if (point < page)
- page = prevline(point);
- if (epage <= point) {
- page = nextline(point);
- i = (page == pos(ebuf) ? LINES-2 : LINES) - helpline;
- while (0 < i--)
- page = prevline(page-1);
- }
- move(helpline, 0);
- i = helpline;
- j = 0;
- epage = page;
- while (1) {
- if (point == epage) {
- row = i;
- col = j;
- }
- p = ptr(epage);
- if (LINES <= i || ebuf <= p)
- break;
- if (*p != '\r') {
- addch(*p);
- j += *p == '\t' ? 8-(j&7) : 1;
- }
- if (*p == '\n' || COLS <= j) {
- ++i;
- j = 0;
- }
- ++epage;
- }
- clrtobot();
- if (++i < LINES)
- mvaddstr(i, 0, "<< EOF >>");
- move(row, col);
- refresh();
- }
-
- int
- main(argc, argv)
- int argc;
- char **argv;
- {
- FILE *fp;
- char *p = *argv;
- int i = (int) strlen(p);
- egap = ebuf = buf + BUF;
- if (argc < 2)
- return (2);
- /* Find basename. */
- while (0 <= i && p[i] != '\\' && p[i] != '/')
- --i;
- p += i+1;
- if (strncmp(p, "ae", 2) == 0 || strncmp(p, "AE", 2) == 0)
- table = modual;
- else if (strncmp(p, "ea", 2) == 0 || strncmp(p, "EA", 2) == 0)
- table = modeless;
- else
- return (2);
- if (initscr() == NULL)
- return (3);
- noecho();
- lineinput(FALSE);
- idlok(stdscr, TRUE);
- initkey();
- fp = fopen(filename = *++argv, "r");
- if (fp != NULL) {
- gap += fread(buf, sizeof (char), (size_t) BUF, fp);
- fclose(fp);
- }
- top();
- help();
- while (!done) {
- display();
- i = 0;
- input = getkey();
- while (table[i].key != 0 && input != table[i].key)
- ++i;
- (*table[i].func)();
- }
- endwin();
- return (0);
- }
-